package org.su.mamamiya.core.ast;

import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
import lombok.Getter;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.Optional;

/**
 * @auther yangchuan
 * @date 2020-03-08 10:56
 */
@Getter
public class AstProccessor {

    private JavacTrees trees;
    private TreeMaker treeMaker;
    private Names names;
    private Types typeUtils;
    private Messager messager;
    private Elements elementUtils;


    public AstProccessor(ProcessingEnvironment processingEnv) {
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
        typeUtils = processingEnv.getTypeUtils();
        messager = processingEnv.getMessager();
        elementUtils = processingEnv.getElementUtils();
    }


    /**
     * 得到注释文档
     *
     * @param element
     * @return
     */
    public String getDoc(Element element) {
        return Optional.ofNullable(elementUtils.getDocComment(element)).orElse("");
    }


    public JCTree memberAccess(Names names, TreeMaker treeMaker, String components) {
        String[] componentArray = components.split("\\.");
        JCTree.JCExpression expr = treeMaker.Ident(names.fromString(componentArray[0]));
        for (int i = 1; i < componentArray.length; i++) {
            expr = treeMaker.Select(expr, names.fromString(componentArray[i]));
        }
        return expr;
    }

    public JCTree memberAccess(String components) {
        return memberAccess(names,treeMaker,components);
    }

    public Name getNameFromString(String s) {
        return names.fromString(s);
    }


    public JCTree getTree(Element element) {
        return trees.getTree(element);
    }

    public JCTree.JCMethodDecl getTree(ExecutableElement element) {
        return trees.getTree(element);
    }

    public void addImportInfo(Element element, String packageStr, String classStr) {
        TreePath treePath = trees.getPath(element);
        Tree leaf = treePath.getLeaf();
        if (treePath.getCompilationUnit() instanceof JCTree.JCCompilationUnit && leaf instanceof JCTree) {
            JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();

            for (JCTree jcTree : jccu.getImports()) {
                if (jcTree != null && jcTree instanceof JCTree.JCImport) {
                    JCTree.JCImport jcImport = (JCTree.JCImport) jcTree;
                    if (jcImport.qualid != null && jcImport.qualid instanceof JCTree.JCFieldAccess) {
                        JCTree.JCFieldAccess jcFieldAccess = (JCTree.JCFieldAccess) jcImport.qualid;
                        try {
                            if (packageStr.equals(jcFieldAccess.selected.toString()) && classStr.equals(jcFieldAccess.name.toString())) {
                                return;
                            }
                        } catch (NullPointerException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            java.util.List<JCTree> trees = new java.util.ArrayList<>();
            trees.addAll(jccu.defs);
            JCTree.JCIdent ident = treeMaker.Ident(names.fromString(packageStr));
            JCTree.JCImport jcImport = treeMaker.Import(treeMaker.Select(
                    ident, names.fromString(classStr)), false);
            if (!trees.contains(jcImport)) {
                trees.add(0, jcImport);
            }
            jccu.defs = List.from(trees);
        }
    }

    public  JCTree.JCAnnotation getAnnotation(JCTree jcTree, List<JCTree.JCExpression> expressions){
        return treeMaker.Annotation(jcTree,expressions);
    }



    public JCTree.JCAssign assignLiteral(String name, JCTree.JCLiteral literal){
        return treeMaker.Assign(treeMaker.Ident(getNameFromString(name)), literal);
    }

    public JCTree.JCAssign assign(String name, Object object){
        return treeMaker.Assign(treeMaker.Ident(getNameFromString(name)), literal(object));
    }

    public JCTree.JCAssign assignObj(String name, JCTree.JCExpression object) {
        return treeMaker.Assign(treeMaker.Ident(getNameFromString(name)), object);
    }

    public JCTree.JCLiteral literal(Object object){
        return treeMaker.Literal(object);
    }
}
